/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grapher;

/**
 *
 * @author Steve Noble
 *
 */
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Font;

import java.util.*;
import java.awt.Color;
import java.awt.Graphics;
import java.util.HashMap;
import java.text.DecimalFormat;

import simframe.simulation.MetricDisplay;
import simframe.simulation.Metric;
import simframe.simulation.Range;

public class MetricViewer extends javax.swing.JPanel implements MetricDisplay {

    private final static int numVTics = 2;
    private final static int chartBottom = 50;
    private  final static int chartTop = 20;
    private final static DecimalFormat timeFormat = new DecimalFormat("year #0");

    private final Map<Metric, List<Double>> metricValues = new HashMap<Metric, List<Double>>();
    private final Map<Metric, Queue<Double>> metricDisplay = new HashMap<Metric, Queue<Double>>();
    private final Map<Metric, Color> metricColor = new HashMap<Metric, Color>();
    private final Map<String, Metric> metricName = new HashMap<String, Metric>();
    
    private final Queue<Long> times = new LinkedList<Long>();
    private int size = 0;

    public Map<Metric, List<Double>> GetMetrics()
    {
        return metricValues;
    }


    class MyFilter extends javax.swing.filechooser.FileFilter {
        public boolean accept(java.io.File file) {
            if(file.isDirectory())
            {
                return true;
            }
            String filename = file.getName();
            return filename.endsWith(".csv");
        }
        public String getDescription() {
            return "*.csv";
        }
    }

    public void printMetricsToFile()
    {
        javax.swing.JFrame frame = new javax.swing.JFrame();
        javax.swing.JFileChooser fc = new javax.swing.JFileChooser();
        MyFilter filter = new MyFilter();
        fc.setFileFilter(filter);
        fc.showSaveDialog(frame);
        java.io.File selFile = fc.getSelectedFile();

        try {
            java.io.FileWriter out = new java.io.FileWriter(selFile);
            java.io.BufferedWriter writer = new java.io.BufferedWriter(out);

            String header = "";
            List<List<Double>> data = new ArrayList<List<Double>>();
            for (Metric metric : metricValues.keySet()) {
                header += metric.getName() + ",";
                data.add(metricValues.get(metric));
            }

            writer.write(header);
            writer.newLine();

            for (int i = 0; i < data.get(0).size(); i += 30) // KMK changed from i++ to i+= 30 to write out once a month
            {
                String line = "";
                for (int j = 0; j < data.size(); j++) {
                    line += data.get(j).get(i) + ",";
                }
                writer.write(line);
                writer.newLine();
            }

            writer.close();
            out.close();
        } catch (java.io.IOException e) {
        }

        javax.swing.JOptionPane.showMessageDialog(null, "Finished Writing Metrics");
    }

     public void addMetric(Metric metric) {

        if (!metricDisplay.containsKey(metric)) {

            metricDisplay.put(metric, new LinkedList<Double>());

            metricName.put(metric.getName(), metric);

            metricValues.put(metric, new ArrayList<Double>());
        }
    }

     public void clearMetrics() {

        synchronized (metricDisplay) {  metricDisplay.clear(); }
        synchronized (metricColor) { metricColor.clear(); }
        synchronized (times) { times.clear(); }
        size = 0;
    }

    public void addDisplay(String name, Color col) {

        Metric metric = metricName.get(name);

        if (metricColor.containsKey(metric)) {
            metricColor.remove(metric);
        }

        metricColor.put(metric, col);

    }

     public void removeDisplay(String name) {

       Metric metric = metricName.get(name);

        if (metricColor.containsKey(metric)) {
            metricColor.remove(metric);
        }
    }

     public synchronized void update(long t) {

        size++;

        boolean remove = size > 2500;

        if (remove) {
            size--;
        }

        synchronized (times) {
            times.add(t / simframe.simulation.Time.Day);

            if (remove) {
                times.remove();
            }
        }

        // update lists
        for (Metric metric : metricDisplay.keySet()) {

            metric.update();
            
            double dval = metric.val();

            Queue<Double> vals = metricDisplay.get(metric);

            synchronized (vals) {
                metricDisplay.get(metric).add(dval);

                if (remove) {
                    vals.remove();
                }
            }

            List<Double> all_vals = metricValues.get(metric);
            synchronized(all_vals)
            {
                all_vals.add(dval);
            }
        }

        repaint();
    }

//    @Override
//    public void repaint() {
//        if (bufferStrategy == null) {
//            createBufferStrategy(2);
//
//            bufferStrategy = this.getBufferStrategy();
//        }
//
//        try {
//        Graphics g = bufferStrategy.getDrawGraphics();
//
//
//        this.update(g);
//
//        bufferStrategy.show();
//        }
//        catch(Exception e) {}
//    }

    @Override
    public void paintComponent(Graphics gen) {

        super.paintComponent(gen);
        
        Graphics2D g = (Graphics2D) gen;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        this.setBackground(Color.WHITE);

        Font font = Font.decode("tahoma-bold-12");

        g.setFont(font);

        int index = 0;
        int w = getWidth();
        int h = getHeight();

        final int numSpace = 45;
        int chartLeft = 5 + numSpace * metricColor.size();

        // draw horizontal tics
        g.setColor(Color.lightGray);
        for (int ii = 0; ii <= numVTics; ++ii) {

            int lineHeight = chartTop + (ii * (h - chartTop - chartBottom) / numVTics);

            g.drawLine(chartLeft, lineHeight, w, lineHeight);
        }

        synchronized (times) {

            int tIndex = 0;
            for (Long t : times) {
                // must map t to screen
                int x0 = w + (tIndex - size);

                if (t % 91 == 0 && x0 > chartLeft) {
                    g.drawLine(x0, chartTop, x0, h - chartBottom);

                    if (t % 364 == 0) {
                        String st = timeFormat.format(t/364);

                        int offset = (int) (st.length() * g.getFont().getSize2D() / 3);
                        // draw a little past the line
                        g.drawString(st, x0 - offset, h + 15 - chartBottom);
                    }
                }

                tIndex++;
            }
        }

        // draw legend

        // draw vertical tics

        int legendOffset = 400;
        for (Metric metric : metricColor.keySet()) {

            DecimalFormat numFormat = new DecimalFormat(metric.getUnits().format());

            Range range = metric.getRange();

            g.setColor(metricColor.get(metric));
            String name = metric.getName() + "  (" + metric.getUnits().toString() + ")";

            g.fillRect(legendOffset, h - 15, 5, 5);

            g.drawString(name, legendOffset + 10 , h - 10);

            legendOffset += (int) (20 + name.length() * g.getFont().getSize2D());

            Queue<Double> vals = metricDisplay.get(metric);
            synchronized (vals) {

                int tIndex = 0;
                int preval = Integer.MIN_VALUE;

                for (Double dval : vals) {

                    // if tIndex = length then x = w
                    int x0 = w + (tIndex - size);

                    int curVal = valueToHeight(range, dval, h);

                    if ((preval > Integer.MIN_VALUE) && (x0 >=  chartLeft)) {
                        //if (x0 >= 0) {
                        g.drawLine(x0, preval, x0 + 1, curVal);
                    }
                    preval = curVal;
                    ++tIndex;
                }
            }

            for (int ii = 0; ii <= numVTics; ++ii) {

                int lineHeight = chartTop + (ii * (h - chartTop - chartBottom) / numVTics);

                String sVal = numFormat.format(heightToValue(range, lineHeight, h));

                // draw at the line
                g.drawString(sVal, 10 + index * numSpace, lineHeight + 2);
            }

            index++;
        }
    }

    private double heightToValue(Range range, int yCoord, int h) {

        return range.invMap(yCoord, h - chartBottom, chartTop);
    }

    private int valueToHeight(Range range, double val, int h) {

        return range.map(val, h - chartBottom, chartTop);
    }
}

